package com.neuronbit.businessflow.runtime;

import com.neuronbit.businessflow.runtime.exec.ExecutedStepInstance;
import com.neuronbit.businessflow.runtime.exec.StepExecutionStatus;
import com.neuronbit.businessflow.runtime.lifecycle.StepLifecycleHook;
import com.neuronbit.businessflow.runtime.repo.BusinessFlowRepository;

import java.sql.SQLException;
import java.time.LocalDateTime;

import static com.neuronbit.businessflow.runtime.exec.StepExecutionStatus.START;

/**
 * The interface Step lifecycle hook.
 */
public class StepLifecycleHookImpl implements StepLifecycleHook {
    private final BusinessFlowRepository repository;

    public StepLifecycleHookImpl(BusinessFlowRepository repository) {
        this.repository = repository;
    }

    /**
     * Support boolean.
     *
     * @param context the context
     * @param flow    the flow
     * @param step    the step
     * @return the boolean
     */
    public boolean support(FlowContext context, Flow flow, Step<?> step) {
        return true;
    }

    /**
     * 判断是否有 step 的执行记录，如果无，则insert 执行记录（包括：执行前context，输入），状态为开始执行，如果有则：
     * 判断flow 是否可重入，如果可重入则判断当前 step 是否已经执行成功，
     * 如果执行失败，则判断 flow是否需要重试执行失败的 step，
     * 如果需要重试则重新拉起 step。否则返回上一次执行失败的结果。
     *
     * @param context the context
     * @param flow    the flow
     * @param tStep   the t step
     */
    public void onStart(FlowContext context, Flow flow, Step tStep) {
        ExecutedStepInstance instance = new ExecutedStepInstance();
        instance.setExecutionId(context.getExecutionId());
        instance.setStepId(tStep.getId());
        instance.setStepName(tStep.getName());
        instance.setStepTypeName(tStep.getClass().getName());
        instance.setContext(GsonUtil.toJson(context));
        instance.setContextTypeName(context.getClass().getName());
        instance.setStatus(START);
        instance.setStartTime(LocalDateTime.now());
        try {
            this.repository.saveStepExecution(instance);
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 更新 step 执行记录：状态=执行完成，输出，执行后的 context，执行耗时
     *
     * @param context the context
     * @param flow    the flow
     * @param tStep   the t step
     */
    public void onEnd(FlowContext context, Flow flow, Step tStep) {
        context.addSucceededStep(tStep);
        ExecutedStepInstance instance = new ExecutedStepInstance();
        instance.setExecutionId(context.getExecutionId());
        instance.setStepId(tStep.getId());
        instance.setStatus(StepExecutionStatus.END);
        instance.setEndTime(LocalDateTime.now());
        try {
            this.repository.updateStepExecutionByExecutionId(instance, START);
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 更新 step 执行记录：状态=执行失败，异常信息，异常堆栈，执行耗时，累加执行次数
     *
     * @param context the context
     * @param flow    the flow
     * @param tStep   the t step
     * @param e       the e
     */
    public void onError(FlowContext context, Flow flow, Step tStep, Exception e) {
        ExecutedStepInstance instance = new ExecutedStepInstance();
        instance.setExecutionId(context.getExecutionId());
        instance.setStepId(tStep.getId());
        instance.setStatus(StepExecutionStatus.ERROR);
        instance.setEndTime(LocalDateTime.now());
        instance.setErrorMessage(e.getMessage());
        try {
            this.repository.updateStepExecutionByExecutionId(instance, START);
        } catch (SQLException ex) {
            throw new RuntimeException(ex);
        }
    }

    /**
     * On skipped.
     *
     * @param context the context
     * @param flow    the flow
     * @param tStep   the t step
     */
    public void onSkipped(FlowContext context, Flow flow, Step tStep) {
        ExecutedStepInstance instance = new ExecutedStepInstance();
        instance.setExecutionId(context.getExecutionId());
        instance.setStepId(tStep.getId());
        instance.setStatus(StepExecutionStatus.SKIPPED);
        instance.setEndTime(LocalDateTime.now());
        try {
            this.repository.updateStepExecutionByExecutionId(instance, START);
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }
    }

    @Override
    public void onPause(FlowContext context, Flow flow, Step tStep) {
        context.addSucceededStep(tStep);
        ExecutedStepInstance instance = new ExecutedStepInstance();
        instance.setExecutionId(context.getExecutionId());
        instance.setStepId(tStep.getId());
        instance.setStatus(StepExecutionStatus.PAUSED);
        instance.setEndTime(LocalDateTime.now());
        try {
            this.repository.updateStepExecutionByExecutionId(instance, START);
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }
    }
}
